package com.kymatrix.kycarbon.xsmd.kbpm.server.core.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kymatrix.kycarbon.xsmd.kbpm.server.base.CommonPage;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.enums.YesNoEnum;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.dto.ProcessDesignPageParam;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.dto.ProcessDesignParam;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.entity.ProcessDesignEntity;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.entity.ProcessDesignHisEntity;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.enums.err.FlowConst;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.service.ProcessDefineService;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.service.ProcessDesignHisService;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.service.ProcessDesignService;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.form.define.PageQueryProcessDefineResp;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.vo.ProcessDesignTypeVO;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.vo.ProcessDesignEntityVO;
import com.kymatrix.kycarbon.xsmd.kbpm.server.dao.ProcessDefineMapper;
import com.kymatrix.kycarbon.xsmd.kbpm.server.dao.ProcessDesignMapper;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.form.design.AddProcessDesignParamReq;
import com.kymatrix.kycarbon.xsmd.system.api.dto.SystemConfigDto;
import com.kymatrix.kycarbon.xsmd.system.api.service.SystemConfigApi;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * <p>
 * 流程设计 服务实现类
 * </p>
 */
@Service
@RequiredArgsConstructor
public class ProcessDesignServiceImpl extends ServiceImpl<ProcessDesignMapper, ProcessDesignEntity> implements ProcessDesignService {
    private final ProcessDesignHisService processDesignHisService;
    private final ProcessDefineService processDefineService;
    private final SystemConfigApi systemConfigApi;
    private final ProcessDefineMapper processDefineMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(AddProcessDesignParamReq req) {
        ProcessDesignEntity processDesignEntity = new ProcessDesignEntity();
        processDesignEntity.setName(req.getName());
        processDesignEntity.setDisplayName(req.getDisplayName());
        processDesignEntity.setType(req.getType());
        processDesignEntity.setIcon(req.getIcon());
        processDesignEntity.setIsDeployed(req.getIsDeployed());
        processDesignEntity.setRemark(req.getRemark());
        
        return super.save(processDesignEntity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(ProcessDesignParam param) {
        ProcessDesignEntity processDesignEntity = new ProcessDesignEntity();
        BeanUtil.copyProperties(param, processDesignEntity);
        ProcessDesignHisEntity latestProcessDesign = processDesignHisService.getLatestByProcessDesignId(param.getId());
        if(latestProcessDesign!=null) {
            JSONObject jsonObject = JSONUtil.parseObj(latestProcessDesign.getContent());
            jsonObject.set(FlowConst.PROCESS_NAME_KEY, processDesignEntity.getName());
            jsonObject.set(FlowConst.PROCESS_DISPLAY_NAME_KEY, processDesignEntity.getDisplayName());
            jsonObject.set(FlowConst.PROCESS_TYPE, processDesignEntity.getType());
            jsonObject.set(FlowConst.PROCESS_DESIGN_ID_KEY, processDesignEntity.getId());
            updateDefine(jsonObject);
        }
        return super.updateById(processDesignEntity);
    }

    @Override
    public CommonPage<ProcessDesignEntityVO> page(ProcessDesignPageParam param) {
        // 按id倒序
        if(StrUtil.isEmpty(param.getOrderBy())) {
            param.setOrderBy("t.id desc");
        }
        IPage<ProcessDesignEntityVO> page = param.buildMpPage();
        QueryWrapper queryWrapper = param.buildQueryWrapper();
        queryWrapper.eq("t.is_deleted", YesNoEnum.NO);
        List<ProcessDesignEntityVO> list = baseMapper.selectCustom(page, queryWrapper);
        page.setRecords(list);
        return CommonPage.toPage(page);
    }
    @Override
    public ProcessDesignEntityVO findById(Long id) {
        ProcessDesignEntityVO vo =  baseMapper.findById(id);
        if(vo!=null) {
            ProcessDesignHisEntity processDesignHisEntity = processDesignHisService.getLatestByProcessDesignId(id);
            if(processDesignHisEntity !=null && ObjectUtil.isNotNull(processDesignHisEntity.getContent())) {
                vo.setJsonObject(JSONUtil.parseObj(Convert.toStr(StrUtil.str(processDesignHisEntity.getContent(), Charset.defaultCharset()),"{")));
            }
            JSONObject jsonObject = vo.getJsonObject();
            if(ObjectUtil.isNull(jsonObject)) {
                jsonObject = new JSONObject();
                vo.setJsonObject(jsonObject);
            }
            if(!jsonObject.containsKey(FlowConst.PROCESS_NAME_KEY)) {
                jsonObject.set(FlowConst.PROCESS_NAME_KEY,vo.getName());
            }
            if(!jsonObject.containsKey(FlowConst.PROCESS_DISPLAY_NAME_KEY)) {
                jsonObject.set(FlowConst.PROCESS_DISPLAY_NAME_KEY,vo.getDisplayName());
            }
        }
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateDefine(JSONObject jsonObject) {
        Long processDesignId = jsonObject.getLong(FlowConst.PROCESS_DESIGN_ID_KEY);
        jsonObject.remove(FlowConst.PROCESS_DESIGN_ID_KEY);
        ProcessDesignHisEntity latestProcessDesign = processDesignHisService.getLatestByProcessDesignId(processDesignId);
        if(latestProcessDesign!=null) {
            JSONObject latestObject = JSONUtil.parseObj(Convert.toStr(StrUtil.str(latestProcessDesign.getContent(), Charset.defaultCharset()),"{"));
            if(ObjectUtil.equals(latestObject,jsonObject)) {
                // 与数据库中最新一条记录进行比较，无变化，则不更新
                return true;
            }
        }
        ProcessDesignHisEntity processDesignHisEntity = new ProcessDesignHisEntity();
        processDesignHisEntity.setProcessDesignId(processDesignId);
        processDesignHisEntity.setContent(JSONUtil.toJsonStr(jsonObject).getBytes(StandardCharsets.UTF_8));
        boolean success = processDesignHisService.save(processDesignHisEntity);
        if(success) {
            // 更新显示name和displayName
            String name = jsonObject.getStr(FlowConst.PROCESS_NAME_KEY);
            String displayName = jsonObject.getStr(FlowConst.PROCESS_DISPLAY_NAME_KEY);
            String type = jsonObject.getStr(FlowConst.PROCESS_TYPE);
            ProcessDesignParam processDesign = new ProcessDesignParam();
            processDesign.setId(processDesignId);
            processDesign.setName(name);
            processDesign.setDisplayName(displayName);
            processDesign.setIsDeployed(YesNoEnum.NO.getCode());
            processDesign.setType(type);
            update(processDesign);
        }
        return  success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deploy(Long processDesignId) {
        ProcessDesignEntityVO processDesign = findById(processDesignId);
        ProcessDesignEntity up = new ProcessDesignEntity();
        up.setId(processDesignId);
        up.setIsDeployed(YesNoEnum.YES.getCode());
        if(updateById(up)) {
            // 先更新状态，更新成功，再部署
            processDefineService.deploy(JSONUtil.toJsonStr(processDesign.getJsonObject()));
        }
    }

    @Override
    public void redeploy(Long processDesignId) {
        ProcessDesignEntityVO processDesign = findById(processDesignId);
        ProcessDesignEntity up = new ProcessDesignEntity();
        up.setId(processDesignId);
        up.setIsDeployed(YesNoEnum.YES.getCode());
        if(updateById(up)) {
            // 取最新流程定义
            PageQueryProcessDefineResp processDefineVO = processDefineService.getLastByName(processDesign.getName());
            // 直接替换
            processDefineService.redeploy(processDefineVO.getId(),JSONUtil.toJsonStr(processDesign.getJsonObject()));
        }
    }

    @Override
    public List<ProcessDesignTypeVO> listByType() {
        List<ProcessDesignTypeVO> res = new ArrayList<>();
        // 先拿到分类
        List<SystemConfigDto> configDtos = systemConfigApi.selectEnabledByGroupName("wf_process_type");
        if(CollectionUtil.isEmpty(configDtos)) return res;
        configDtos.forEach(dto -> {
            ProcessDesignTypeVO typeVO = new ProcessDesignTypeVO();
            typeVO.setType(dto.getValue());
            typeVO.setTitle(dto.getKey());
            typeVO.setItems(new ArrayList<>());
            // 根据分类查询流程设计
            List<ProcessDesignEntity> processDesignEntityList = baseMapper.selectList(Wrappers.lambdaQuery(ProcessDesignEntity.class).eq(ProcessDesignEntity::getType,typeVO.getType()));
            if(CollectionUtil.isNotEmpty(processDesignEntityList)) {
                processDesignEntityList.forEach(processDesign->{
                    // 要以已部署的为准，所以查询最新版的流程定义信息
                    PageQueryProcessDefineResp processDefineVO = processDefineMapper.selectLastByName(processDesign.getName());
                    if(processDefineVO!=null) {
                        ProcessDesignEntityVO processDesignVO = BeanUtil.toBean(processDesign, ProcessDesignEntityVO.class);
                        processDesignVO.setProcessDefineId(processDefineVO.getId());
                        processDesignVO.setName(processDefineVO.getName());
                        processDesignVO.setDisplayName(processDefineVO.getDisplayName());
                        processDefineVO.setJsonObject(JSONUtil.parseObj(processDefineVO.getContent()));
                        processDesignVO.setIcon(processDesign.getIcon());
                        typeVO.getItems().add(processDesignVO);
                    }
                });
            }
            res.add(typeVO);
        });
        return res;
    }
}
